/*-------------------------------------------------------------------------
 *
 * tsvector.c
 *	  tsvector 的 I/O 函数
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/tsvector.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "libpq/pqformat.h"
#include "tsearch/ts_locale.h"
#include "tsearch/ts_utils.h"
#include "utils/builtins.h"
#include "utils/memutils.h"

typedef struct
{
	WordEntry	entry;			/* 必须放在第一位！ */
	WordEntryPos *pos;
	int			poslen;			/* pos 中元素的数量 */
} WordEntryIN;


/* 对两个 WordEntryPos 值进行 qsort 比较 */
int compareWordEntryPos(const void *fc_a, const void *fc_b)
{
	int			fc_apos = WEP_GETPOS(*(const WordEntryPos *) fc_a);
	int			fc_bpos = WEP_GETPOS(*(const WordEntryPos *) fc_b);

	if (fc_apos == fc_bpos)
		return 0;
	return (fc_apos > fc_bpos) ? 1 : -1;
}

/*
 * 移除重复的 pos 条目。如果有两个相同 pos 但
 * 权重不同的条目，保留更高的权重，因此我们不能在这里使用
 * qunique。
 *
 * 返回新的长度。
 */
static int fc_uniquePos(WordEntryPos *fc_a, int fc_l)
{
	WordEntryPos *fc_ptr,
			   *fc_res;

	if (fc_l <= 1)
		return fc_l;

	qsort((void *) fc_a, fc_l, sizeof(WordEntryPos), compareWordEntryPos);

	fc_res = fc_a;
	fc_ptr = fc_a + 1;
	while (fc_ptr - fc_a < fc_l)
	{
		if (WEP_GETPOS(*fc_ptr) != WEP_GETPOS(*fc_res))
		{
			fc_res++;
			*fc_res = *fc_ptr;
			if (fc_res - fc_a >= MAXNUMPOS - 1 ||
				WEP_GETPOS(*fc_res) == MAXENTRYPOS - 1)
				break;
		}
		else if (WEP_GETWEIGHT(*fc_ptr) > WEP_GETWEIGHT(*fc_res))
			WEP_SETWEIGHT(*fc_res, WEP_GETWEIGHT(*fc_ptr));
		fc_ptr++;
	}

	return fc_res + 1 - fc_a;
}

/* 对两个 WordEntryIN 值进行 qsort 比较 */
static int fc_compareentry(const void *fc_va, const void *fc_vb, void *fc_arg)
{
	const WordEntryIN *fc_a = (const WordEntryIN *) fc_va;
	const WordEntryIN *fc_b = (const WordEntryIN *) fc_vb;
	char	   *fc_BufferStr = (char *) fc_arg;

	return tsCompareString(&fc_BufferStr[fc_a->entry.pos], fc_a->entry.len,
						   &fc_BufferStr[fc_b->entry.pos], fc_b->entry.len,
						   false);
}

/*
 * 对 WordEntryIN 数组进行排序，移除重复项。
 * *outbuflen 接收字符串和位置所需的空间量。
 */
static int fc_uniqueentry(WordEntryIN *fc_a, int fc_l, char *fc_buf, int *fc_outbuflen)
{
	int			fc_buflen;
	WordEntryIN *fc_ptr,
			   *fc_res;

	Assert(fc_l >= 1);

	if (fc_l > 1)
		qsort_arg((void *) fc_a, fc_l, sizeof(WordEntryIN), fc_compareentry,
				  (void *) fc_buf);

	fc_buflen = 0;
	fc_res = fc_a;
	fc_ptr = fc_a + 1;
	while (fc_ptr - fc_a < fc_l)
	{
		if (!(fc_ptr->entry.len == fc_res->entry.len &&
			  strncmp(&fc_buf[fc_ptr->entry.pos], &fc_buf[fc_res->entry.pos],
					  fc_res->entry.len) == 0))
		{
			/* 完成数据累积到 *res，计算所需空间 */
			fc_buflen += fc_res->entry.len;
			if (fc_res->entry.haspos)
			{
				fc_res->poslen = fc_uniquePos(fc_res->pos, fc_res->poslen);
				fc_buflen = SHORTALIGN(fc_buflen);
				fc_buflen += fc_res->poslen * sizeof(WordEntryPos) + sizeof(uint16);
			}
			fc_res++;
			if (fc_res != fc_ptr)
				memcpy(fc_res, fc_ptr, sizeof(WordEntryIN));
		}
		else if (fc_ptr->entry.haspos)
		{
			if (fc_res->entry.haspos)
			{
				/* 将 ptr 的位置附加到 res 的位置 */
				int			fc_newlen = fc_ptr->poslen + fc_res->poslen;

				fc_res->pos = (WordEntryPos *)
					repalloc(fc_res->pos, fc_newlen * sizeof(WordEntryPos));
				memcpy(&fc_res->pos[fc_res->poslen], fc_ptr->pos,
					   fc_ptr->poslen * sizeof(WordEntryPos));
				fc_res->poslen = fc_newlen;
				pfree(fc_ptr->pos);
			}
			else
			{
				/* 直接将 ptr 的位置赋给 pos */
				fc_res->entry.haspos = 1;
				fc_res->pos = fc_ptr->pos;
				fc_res->poslen = fc_ptr->poslen;
			}
		}
		fc_ptr++;
	}

	/* 计算最后一个项所需的空间 */
	fc_buflen += fc_res->entry.len;
	if (fc_res->entry.haspos)
	{
		fc_res->poslen = fc_uniquePos(fc_res->pos, fc_res->poslen);
		fc_buflen = SHORTALIGN(fc_buflen);
		fc_buflen += fc_res->poslen * sizeof(WordEntryPos) + sizeof(uint16);
	}

	*fc_outbuflen = fc_buflen;
	return fc_res + 1 - fc_a;
}

static int fc_WordEntryCMP(WordEntry *fc_a, WordEntry *fc_b, char *fc_buf)
{
	return fc_compareentry(fc_a, fc_b, fc_buf);
}


Datum tsvectorin(PG_FUNCTION_ARGS)
{
	char	   *fc_buf = PG_GETARG_CSTRING(0);
	TSVectorParseState fc_state;
	WordEntryIN *fc_arr;
	int			fc_totallen;
	int			fc_arrlen;			/* arr 的分配大小 */
	WordEntry  *fc_inarr;
	int			fc_len = 0;
	TSVector	fc_in;
	int			fc_i;
	char	   *fc_token;
	int			fc_toklen;
	WordEntryPos *fc_pos;
	int			fc_poslen;
	char	   *fc_strbuf;
	int			fc_stroff;

	/*
	 * 令牌被附加到 tmpbuf，cur 是指向 tmpbuf 中已使用空间的末尾的指针。
	 */
	char	   *fc_tmpbuf;
	char	   *fc_cur;
	int			fc_buflen = 256;	/* tmpbuf 的分配大小 */

	fc_state = init_tsvector_parser(fc_buf, 0);

	fc_arrlen = 64;
	fc_arr = (WordEntryIN *) palloc(sizeof(WordEntryIN) * fc_arrlen);
	fc_cur = fc_tmpbuf = (char *) palloc(fc_buflen);

	while (gettoken_tsvector(fc_state, &fc_token, &fc_toklen, &fc_pos, &fc_poslen, NULL))
	{
		if (fc_toklen >= MAXSTRLEN)
			ereport(ERROR,
					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
					 errmsg("word is too long (%ld bytes, max %ld bytes)",
							(long) fc_toklen,
							(long) (MAXSTRLEN - 1))));

		if (fc_cur - fc_tmpbuf > MAXSTRPOS)
			ereport(ERROR,
					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
					 errmsg("string is too long for tsvector (%ld bytes, max %ld bytes)",
							(long) (fc_cur - fc_tmpbuf), (long) MAXSTRPOS)));

		/*
		 * 如果需要，增大缓冲区
		 */
		if (fc_len >= fc_arrlen)
		{
			fc_arrlen *= 2;
			fc_arr = (WordEntryIN *)
				repalloc((void *) fc_arr, sizeof(WordEntryIN) * fc_arrlen);
		}
		while ((fc_cur - fc_tmpbuf) + fc_toklen >= fc_buflen)
		{
			int			fc_dist = fc_cur - fc_tmpbuf;

			fc_buflen *= 2;
			fc_tmpbuf = (char *) repalloc((void *) fc_tmpbuf, fc_buflen);
			fc_cur = fc_tmpbuf + fc_dist;
		}
		fc_arr[fc_len].entry.len = fc_toklen;
		fc_arr[fc_len].entry.pos = fc_cur - fc_tmpbuf;
		memcpy((void *) fc_cur, (void *) fc_token, fc_toklen);
		fc_cur += fc_toklen;

		if (fc_poslen != 0)
		{
			fc_arr[fc_len].entry.haspos = 1;
			fc_arr[fc_len].pos = fc_pos;
			fc_arr[fc_len].poslen = fc_poslen;
		}
		else
		{
			fc_arr[fc_len].entry.haspos = 0;
			fc_arr[fc_len].pos = NULL;
			fc_arr[fc_len].poslen = 0;
		}
		fc_len++;
	}

	close_tsvector_parser(fc_state);

	if (fc_len > 0)
		fc_len = fc_uniqueentry(fc_arr, fc_len, fc_tmpbuf, &fc_buflen);
	else
		fc_buflen = 0;

	if (fc_buflen > MAXSTRPOS)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("string is too long for tsvector (%d bytes, max %d bytes)", fc_buflen, MAXSTRPOS)));

	fc_totallen = CALCDATASIZE(fc_len, fc_buflen);
	fc_in = (TSVector) palloc0(fc_totallen);
	SET_VARSIZE(fc_in, fc_totallen);
	fc_in->size = fc_len;
	fc_inarr = ARRPTR(fc_in);
	fc_strbuf = STRPTR(fc_in);
	fc_stroff = 0;
	for (fc_i = 0; fc_i < fc_len; fc_i++)
	{
		memcpy(fc_strbuf + fc_stroff, &fc_tmpbuf[fc_arr[fc_i].entry.pos], fc_arr[fc_i].entry.len);
		fc_arr[fc_i].entry.pos = fc_stroff;
		fc_stroff += fc_arr[fc_i].entry.len;
		if (fc_arr[fc_i].entry.haspos)
		{
			if (fc_arr[fc_i].poslen > 0xFFFF)
				elog(ERROR, "positions array too long");

			/* 复制位置的数量 */
			fc_stroff = SHORTALIGN(fc_stroff);
			*(uint16 *) (fc_strbuf + fc_stroff) = (uint16) fc_arr[fc_i].poslen;
			fc_stroff += sizeof(uint16);

			/* 复制位置 */
			memcpy(fc_strbuf + fc_stroff, fc_arr[fc_i].pos, fc_arr[fc_i].poslen * sizeof(WordEntryPos));
			fc_stroff += fc_arr[fc_i].poslen * sizeof(WordEntryPos);

			pfree(fc_arr[fc_i].pos);
		}
		fc_inarr[fc_i] = fc_arr[fc_i].entry;
	}

	Assert((fc_strbuf + fc_stroff - (char *) fc_in) == fc_totallen);

	PG_RETURN_TSVECTOR(fc_in);
}

Datum tsvectorout(PG_FUNCTION_ARGS)
{
	TSVector	fc_out = PG_GETARG_TSVECTOR(0);
	char	   *fc_outbuf;
	int32		fc_i,
				fc_lenbuf = 0,
				fc_pp;
	WordEntry  *fc_ptr = ARRPTR(fc_out);
	char	   *fc_curbegin,
			   *fc_curin,
			   *fc_curout;

	fc_lenbuf = fc_out->size * 2 /* '' */ + fc_out->size - 1 /* space */ + 2 /* \0 */ ;
	for (fc_i = 0; fc_i < fc_out->size; fc_i++)
	{
		fc_lenbuf += fc_ptr[fc_i].len * 2 * pg_database_encoding_max_length() /* 为转义 */ ;
		if (fc_ptr[fc_i].haspos)
			fc_lenbuf += 1 /* : */ + 7 /* int2 + , + 权重 */ * POSDATALEN(fc_out, &(fc_ptr[fc_i]));
	}

	fc_curout = fc_outbuf = (char *) palloc(fc_lenbuf);
	for (fc_i = 0; fc_i < fc_out->size; fc_i++)
	{
		fc_curbegin = fc_curin = STRPTR(fc_out) + fc_ptr->pos;
		if (fc_i != 0)
			*fc_curout++ = ' ';
		*fc_curout++ = '\'';
		while (fc_curin - fc_curbegin < fc_ptr->len)
		{
			int			fc_len = pg_mblen(fc_curin);

			if (t_iseq(fc_curin, '\''))
				*fc_curout++ = '\'';
			else if (t_iseq(fc_curin, '\\'))
				*fc_curout++ = '\\';

			while (fc_len--)
				*fc_curout++ = *fc_curin++;
		}

		*fc_curout++ = '\'';
		if ((fc_pp = POSDATALEN(fc_out, fc_ptr)) != 0)
		{
			WordEntryPos *fc_wptr;

			*fc_curout++ = ':';
			fc_wptr = POSDATAPTR(fc_out, fc_ptr);
			while (fc_pp)
			{
				fc_curout += sprintf(fc_curout, "%d", WEP_GETPOS(*fc_wptr));
				switch (WEP_GETWEIGHT(*fc_wptr))
				{
					case 3:
						*fc_curout++ = 'A';
						break;
					case 2:
						*fc_curout++ = 'B';
						break;
					case 1:
						*fc_curout++ = 'C';
						break;
					case 0:
					default:
						break;
				}

				if (fc_pp > 1)
					*fc_curout++ = ',';
				fc_pp--;
				fc_wptr++;
			}
		}
		fc_ptr++;
	}

	*fc_curout = '\0';
	PG_FREE_IF_COPY(fc_out, 0);
	PG_RETURN_CSTRING(fc_outbuf);
}

/*
 * 二进制输入/输出函数。二进制格式如下：
 *
 * uint32	词素数量
 *
 * 对于每个词素：
 *		词素文本以客户端编码结束，空字符终止
 *		uint16	位置数量
 *		对于每个位置：
 *			uint16 WordEntryPos
 */

Datum tsvectorsend(PG_FUNCTION_ARGS)
{
	TSVector	fc_vec = PG_GETARG_TSVECTOR(0);
	StringInfoData fc_buf;
	int			fc_i,
				fc_j;
	WordEntry  *fc_weptr = ARRPTR(fc_vec);

	pq_begintypsend(&fc_buf);

	pq_sendint32(&fc_buf, fc_vec->size);
	for (fc_i = 0; fc_i < fc_vec->size; fc_i++)
	{
		uint16		fc_npos;

		/*
		 * TSVector 数组中的字符串不是以 null 终止的，因此我们
		 * 必须单独发送 null 终止符
		 */
		pq_sendtext(&fc_buf, STRPTR(fc_vec) + fc_weptr->pos, fc_weptr->len);
		pq_sendbyte(&fc_buf, '\0');

		fc_npos = POSDATALEN(fc_vec, fc_weptr);
		pq_sendint16(&fc_buf, fc_npos);

		if (fc_npos > 0)
		{
			WordEntryPos *fc_wepptr = POSDATAPTR(fc_vec, fc_weptr);

			for (fc_j = 0; fc_j < fc_npos; fc_j++)
				pq_sendint16(&fc_buf, fc_wepptr[fc_j]);
		}
		fc_weptr++;
	}

	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}

Datum tsvectorrecv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	TSVector	fc_vec;
	int			fc_i;
	int32		fc_nentries;
	int			fc_datalen;		/* 固定大小 TSVector 头部和
								 * WordEntries 之后可变大小区域使用的字节数 */
	Size		fc_hdrlen;
	Size		fc_len;			/* vec 的分配大小 */
	bool		fc_needSort = false;

	fc_nentries = pq_getmsgint(fc_buf, sizeof(int32));
	if (fc_nentries < 0 || fc_nentries > (MaxAllocSize / sizeof(WordEntry)))
		elog(ERROR, "invalid size of tsvector");

	fc_hdrlen = DATAHDRSIZE + sizeof(WordEntry) * fc_nentries;

	fc_len = fc_hdrlen * 2;			/* 乘以二以为词素腾出空间 */
	fc_vec = (TSVector) palloc0(fc_len);
	fc_vec->size = fc_nentries;

	fc_datalen = 0;
	for (fc_i = 0; fc_i < fc_nentries; fc_i++)
	{
		const char *fc_lexeme;
		uint16		fc_npos;
		size_t		fc_lex_len;

		fc_lexeme = pq_getmsgstring(fc_buf);
		fc_npos = (uint16) pq_getmsgint(fc_buf, sizeof(uint16));

		/* 运行检查 */

		fc_lex_len = strlen(fc_lexeme);
		if (fc_lex_len > MAXSTRLEN)
			elog(ERROR, "invalid tsvector: lexeme too long");

		if (fc_datalen > MAXSTRPOS)
			elog(ERROR, "invalid tsvector: maximum total lexeme length exceeded");

		if (fc_npos > MAXNUMPOS)
			elog(ERROR, "unexpected number of tsvector positions");

		/*
		 * 看起来有效。填充 WordEntry 结构，并复制词素。
		 *
		 * 但请确保缓冲区足够大。
		 */
		while (fc_hdrlen + SHORTALIGN(fc_datalen + fc_lex_len) +
			   sizeof(uint16) + fc_npos * sizeof(WordEntryPos) >= fc_len)
		{
			fc_len *= 2;
			fc_vec = (TSVector) repalloc(fc_vec, fc_len);
		}

		fc_vec->entries[fc_i].haspos = (fc_npos > 0) ? 1 : 0;
		fc_vec->entries[fc_i].len = fc_lex_len;
		fc_vec->entries[fc_i].pos = fc_datalen;

		memcpy(STRPTR(fc_vec) + fc_datalen, fc_lexeme, fc_lex_len);

		fc_datalen += fc_lex_len;

		if (fc_i > 0 && fc_WordEntryCMP(&fc_vec->entries[fc_i],
								  &fc_vec->entries[fc_i - 1],
								  STRPTR(fc_vec)) <= 0)
			fc_needSort = true;

		/* 接收位置 */
		if (fc_npos > 0)
		{
			uint16		fc_j;
			WordEntryPos *fc_wepptr;

			/*
			 * 如果有必要，填充到2字节对齐。尽管我们对初始分配使用了palloc0，
			 * 但后续的repalloc内存区域并未初始化为零。
			 */
			if (fc_datalen != SHORTALIGN(fc_datalen))
			{
				*(STRPTR(fc_vec) + fc_datalen) = '\0';
				fc_datalen = SHORTALIGN(fc_datalen);
			}

			memcpy(STRPTR(fc_vec) + fc_datalen, &fc_npos, sizeof(uint16));

			fc_wepptr = POSDATAPTR(fc_vec, &fc_vec->entries[fc_i]);
			for (fc_j = 0; fc_j < fc_npos; fc_j++)
			{
				fc_wepptr[fc_j] = (WordEntryPos) pq_getmsgint(fc_buf, sizeof(WordEntryPos));
				if (fc_j > 0 && WEP_GETPOS(fc_wepptr[fc_j]) <= WEP_GETPOS(fc_wepptr[fc_j - 1]))
					elog(ERROR, "position information is misordered");
			}

			fc_datalen += sizeof(uint16) + fc_npos * sizeof(WordEntryPos);
		}
	}

	SET_VARSIZE(fc_vec, fc_hdrlen + fc_datalen);

	if (fc_needSort)
		qsort_arg((void *) ARRPTR(fc_vec), fc_vec->size, sizeof(WordEntry),
				  fc_compareentry, (void *) STRPTR(fc_vec));

	PG_RETURN_TSVECTOR(fc_vec);
}
